import { App, Config, Init, Provide } from '@midwayjs/decorator'
import { Application } from 'egg'
import { customAlphabet } from 'nanoid'
import { gql } from 'graphql-request'

import { getTimestamptz } from '../utils/helper'

const Core = require('@alicloud/pop-core')
const dayjs = require('dayjs')

@Provide()
export class SmsService {

  protected client: any
  protected nanoid: () => string

  @Config('sms')
  smsConfig: any

  @App()
  app: Application

  @Init()
  async init() {
    try {
      const {
        accessKeyId,
        accessKeySecret
      } = this.smsConfig

      this.client = new Core({
        accessKeyId,
        accessKeySecret,
        endpoint: 'https://dysmsapi.aliyuncs.com',
        apiVersion: '2017-05-25'
      })
    } catch (err) {
      this.app.logger.error(err)
    }

    this.nanoid = customAlphabet('1234567890', 6)
  }

  async get(phone) {
    const {
      ie_sms
    } = await this.app.gqlRequest(gql`
      query ($phone: String!) {
        ie_sms (where: { phone: { _eq: $phone } }) {
          id
          code
          phone
          expire_at
          create_at
        }
      }
    `, {
      phone
    })

    if (ie_sms.length) {
      return ie_sms[0]
    }
  }

  async send(phone) {
    const existed_sms = await this.get(phone)

    if (existed_sms) {
      const create_at = getTimestamptz(existed_sms.create_at)

      if (Date.now() - create_at < 60 * 1e3) return
    }

    const code = this.nanoid()
    const {
      SignName,
      TemplateCode
    } = this.smsConfig

    const params = {
      PhoneNumbers: phone,
      SignName,
      TemplateCode,
      TemplateParam: JSON.stringify({ code })
    }

    // send request to aliyun
    try {
      await this.client.request('SendSms', params, {
        method: 'POST'
      })

      await this.app.gqlRequest(gql`
        mutation ($sms: ie_sms_insert_input!) {
          insert_ie_sms (
            objects: [$sms],
            on_conflict: {
                # save on hasura console
                constraint: ie_sms_phone_key
                update_columns: [code, expire_at, create_at]
              }
          ) {
            affected_rows
          }
        }
      `, {
        sms: {
          phone,
          code,
          // now() + interval '5 minutes' not work
          expire_at: dayjs().add(5, 'm').toISOString()
        }
      })

      return code
    } catch (err) {
      this.app.logger.error(err)
    }
  }

  async validate(phone, code) {
    const existed_sms = await this.get(phone)

    if (!existed_sms) {
      return
    }

    const expire_at = getTimestamptz(existed_sms.expire_at)

    if (expire_at < Date.now()) {
      await this.app.gqlRequest(gql`
        mutation ($id: Int!) {
          delete_ie_sms_by_pk (id: $id) {
            id
          }
        }
      `, {
        id: existed_sms.id
      })

      return
    }

    return existed_sms.code === String(code)
  }
}
